/**
 * Created by Jing on 2018-02-26.
 */
import {
  select, call, put, all, fork, cancel, takeEvery,
} from 'redux-saga/effects'
import { delay } from 'redux-saga'
import { validForm as validFormDuck } from 'ducks'
import { ValidType } from 'components/validForm'
import { gqlQuery } from './utils'

const tasks = {}

function* validMatch(rule, value) {
  const refValue = yield select(validFormDuck.selectors.fieldValue, rule.value)
  return { response: refValue === value }
}

function* validRemote(rule, value) {
  const result = yield call(gqlQuery, rule.value.query, Object.assign(rule.value.params, { value }))
  return { response: rule.getResult(result) }
}

function validNumber(rule, value) {
  let result = true
  if (rule.value.max) {
    result = result && value <= rule.value.max
  }
  if (rule.value.min) {
    result = result && value >= rule.value.min
  }
  return { response: result }
}

function validEQ(rule, value) {
  let result = true
  result = value === rule.value
  return { response: result }
}

function* validRefed(rule, value) {
  const refValue = yield select(validFormDuck.selectors.fieldValue, rule.value)
  let state = 'success'
  if (refValue !== undefined && refValue !== '') {
    if (refValue !== value) {
      state = 'error'
    }
    yield put(validFormDuck.creators.actionSetFieldState(rule.value, state))
    if (state === 'error') {
      yield put(validFormDuck.creators.actionSetFieldErrorMessage(rule.value, rule.errorMessage))
    }
  }
  return { response: true }
}

function validMAXLENGTH(rule, value) {
  let result = true
  result = value === undefined || value.length <= rule.value
  return { response: result }
}

function* validOneRule(rule, value) {
  switch (rule.type) {
    case ValidType.REGULAR:
      return { response: rule.value.test(value) }
    case ValidType.MATCH:
      return yield validMatch(rule, value)
    case ValidType.REMOTE:
      return yield validRemote(rule, value)
    case ValidType.REFED:
      return yield validRefed(rule, value)
    case ValidType.NUMBER:
      return validNumber(rule, value)
    case ValidType.EQ:
      return validEQ(rule, value)
    case ValidType.MAXLENGTH:
      return validMAXLENGTH(rule, value)
    default:
      return { error: 'not support this rule type' }
  }
}

export function* validOneField(value, rules) {
  for (let i = 0; i < rules.length; i += 1) {
    const rule = rules[i]
    const { response, error } = yield call(validOneRule, rule, value)
    console.log(response, error)
    if (response !== undefined) {
      if (response === false || response.status === 'error') {
        return {
          response: {
            status: 'error',
            message: rule.errorMessage,
          },
        }
      }
    }
    if (error) {
      return { error }
    }
  }
  return {
    response: {
      status: 'success',
    },
  }
}

function* inputChange(action) {
  yield call(delay, 1500)
  const { response } = yield call(validOneField, action.value, action.rules)
  if (response) {
    yield put(validFormDuck.creators.actionSetFieldState(action.fieldName, response.status))
    if (response.status === 'error') {
      yield put(
        validFormDuck.creators.actionSetFieldErrorMessage(action.fieldName, response.message),
      )
    }
  }
}

function* inputChangeDispatch(action) {
  const initValue = yield select(validFormDuck.selectors.fieldInitValue, action.fieldName)
  const fieldTask = tasks[action.fieldName]
  if (fieldTask) {
    yield cancel(fieldTask) // cancel is no-op if the task has already terminated
  }
  if (initValue !== action.value) {
    if (action.rules) {
      const lastTask = yield fork(inputChange, action)
      tasks[action.fieldName] = lastTask
    } else {
      yield put(validFormDuck.creators.actionSetFieldState(action.fieldName, 'success'))
    }
  }
}

export default function* sagas() {
  yield all([takeEvery(validFormDuck.types.FIELD_CHANGE, inputChangeDispatch)])
}
